package com.lucenten.collect.gnss.handler.impl;

import cn.quevo.common.lang.DateFormatUtils;
import cn.quevo.common.lang.NumberUtils;
import cn.quevo.common.lang.StringUtils;
import com.lucenten.collect.gnss.entity.GnssFile;
import com.lucenten.collect.gnss.entity.SatelliteStation;
import com.lucenten.collect.gnss.entity.StecpTeclTrans;
import com.lucenten.collect.gnss.enums.GnssFormat;
import com.lucenten.collect.gnss.enums.GnssType;
import com.lucenten.collect.gnss.enums.STecType;
import com.lucenten.collect.gnss.handler.AbstractGnssHandler;
import com.lucenten.collect.gnss.handler.GnssHandler;
import com.lucenten.collect.gnss.handler.files.HardwareDelay;
import com.lucenten.collect.gnss.handler.tools.GnssContext;
import com.lucenten.collect.gnss.service.ObsErvationService;
import com.lucenten.collect.gnss.service.SatelliteStationService;
import com.lucenten.collect.utils.FileUtils;
import com.lucenten.mq.SendJmsInfo;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 第八步：STEC合并(2个小时)
 */
@Slf4j
public class STecHoureGnssHandler extends AbstractGnssHandler implements GnssHandler {
    private final String PRE = "STEC_";
    private double[] vals;

    public STecHoureGnssHandler(SatelliteStationService satelliteStationService, ObsErvationService obsErvationService) {
        super(satelliteStationService, obsErvationService);
    }

    @Override
    protected boolean doProcess(GnssFile gnssFile) {
        Path biasPath = Paths.get(COLLECT_ROOT,gnssFile.getRealType(),gnssFile.getGnssType().getPath(),GnssFormat.BIAS.getName());
        Map<String,double[]> delayMap = HardwareDelay.getBias(biasPath.normalize().toString(), gnssFile.getDate());
        if (!StringUtils.isEmpty(delayMap) && delayMap.containsKey(gnssFile.getReStationName())) {
            this.vals = delayMap.get(gnssFile.getReStationName());
        }
        if (gnssFile.getReStationName().equalsIgnoreCase("bfqe")) {
            System.out.println("dd");
        }
        String root = super.getDataFormatPath(GnssFormat.TECPTECL, gnssFile);
        for (STecType sTecType : STecType.values()) {
            this.saveStec(gnssFile, sTecType, root);
        }
        return false;
    }

    private void saveStec(GnssFile gnssFile, STecType sTecType, String root) {
        StringBuilder filePre = new StringBuilder("TECPTECL_").append(sTecType.getName()).append("_").append(gnssFile.getReStationName());
        List<StecpTeclTrans> stecList = this.getSatellite(Paths.get(root), filePre.toString());
        if (StringUtils.isEmpty(stecList)) {
            return;
        }
        Path savePath = null;
        int[] prns = stecList.stream().mapToInt(StecpTeclTrans::getPrn).sorted().distinct().toArray();
        try {
            filePre = new StringBuilder(PRE).append(sTecType.getName()).append("_").append(gnssFile.getReStationName());
            filePre.append(gnssFile.getYear().toString()).append(NumberUtils.format(gnssFile.getDayOfYear(),"000"));
            filePre.append(GnssContext.TIME_CODE[gnssFile.getHoure()]).append(".dat");
            savePath = Paths.get(super.getDataFormatPath(GnssFormat.STEC, gnssFile),filePre.toString());
            FileUtils.createDirs(savePath);
            List<StecpTeclTrans> waiteList = null;
            List<String> title = new ArrayList<>();
            LocalDateTime max = stecList.stream().map(StecpTeclTrans::getLocalDateTime).max(LocalDateTime::compareTo).get();
            LocalDateTime min = stecList.stream().map(StecpTeclTrans::getLocalDateTime).min(LocalDateTime::compareTo).get();
            title.add("% Station:  "+ gnssFile.getReStationName());
            title.add("% Latitude and Longitude:    "+String.format("%.2f",gnssFile.getStation().getLatitude())+"  "+String.format("%.2f",gnssFile.getStation().getLongitude()));
            title.add("% Start time:  "+ DateFormatUtils.format(min.toInstant(ZoneOffset.UTC),"yyyy  MM  dd  HH  mm  ss"));
            title.add("% End Time:    "+ DateFormatUtils.format(max.toInstant(ZoneOffset.UTC),"yyyy  MM  dd  HH  mm  ss"));
            title.add("% Promulgator: Institute of Geology and Geophysics, CAS");
            title.add("% Unit: ELE=degree;AZI=degree;TECP=TECU;TECL=TECU;");
            title.add("% Soft version: V1.0");
            title.add("%");
            //if(map != null){
            title.add("% yyyy MM dd HH mm ss   PRN       AZI       ELE      TECP      TECL      STEC               L1               L2      VTEC");
            title.add("% -----------------------------------------------------------------------------------------------------------------------");
            Files.write(savePath,title);
            for (int i = 0; i < prns.length; i ++) {
                final int index = i;
                waiteList = splitDateTime(stecList.stream().filter(stecpTeclTrans -> stecpTeclTrans.getPrn().equals(prns[index])).filter(stecpTeclTrans -> (vals == null || vals.length <= 0 || stecpTeclTrans.getVtec() >= 0)).collect(Collectors.toList()));
                Files.write(savePath, waiteList.stream().map(stecpTeclTrans -> stecpTeclTrans.getJoin()).collect(Collectors.toList()),StandardOpenOption.APPEND);
            }
//            SendJmsInfo.sendGnssMq(15, savePath.getFileName().normalize().toString(), 0);
//            SendJmsInfo.sendGnssMq(16, savePath.getFileName().normalize().toString(), 0);
        } catch (IOException e) {
            log.error("文件保存失败",e);
            SendJmsInfo.sendGnssMq(15, savePath == null ? "" : savePath.getFileName().normalize().toString(), 0);
            SendJmsInfo.sendGnssMq(16, savePath == null ? "" : savePath.getFileName().normalize().toString(), 0);
        }
    }

    /**
     * 获取目录下所有符合条件的文件列表
     *
     * @param filePath
     * @param startName
     * @return
     */
    private List<StecpTeclTrans> getSatellite(Path filePath, String startName){
        if (Files.notExists(filePath)) {
            return new ArrayList<>();
        }
        List<String> tmpList = new ArrayList<>();
        List<StecpTeclTrans> satellite = new ArrayList<>();
        try (Stream<Path> files = Files.list(filePath).filter(file -> !Files.isDirectory(file) && file.getFileName().toString().startsWith(startName))) {
            files.forEach(file -> {
                try(Stream<String> lines = Files.lines(file);) {
                    lines.forEach(line -> {
                        if (!StringUtils.isBlank(line)) {
                            satellite.add(StecpTeclTrans.builderLine(line));
                            tmpList.add(line);
                        }
                    });
                } catch (IOException e) {

                }
            });
            // 先按卫星排序，然后再按时间排序
            return satellite.stream().sorted(Comparator.comparing(StecpTeclTrans::getPrn).thenComparing(StecpTeclTrans::getLocalDateTime)).collect(Collectors.toList());
//            return files.collect(Collectors.toList());
        } catch (IOException e) {
            log.error("文件读取失败",e);
        }
        return new ArrayList<>();
    }
    /**
     * 验证时间是否是连续的，如果时间不连续，则从不连续的地方断开处理；
     * 如果连续数量不足，则直接忽略
     * @param list
     * @return
     */
    private List<StecpTeclTrans> splitDateTime(List<StecpTeclTrans> list) {
        if (StringUtils.isEmpty(list) || list.size() < GnssContext.LINK_DATA_NUM) {
            return new ArrayList<>();
        }
        List<StecpTeclTrans> continuous = new ArrayList<>();
        List<StecpTeclTrans> blockList = new ArrayList<>();
        long afterTime = 0;
        long beforeTime = 0;
        StecpTeclTrans stecpTeclTrans = null;
        int i = 0, max = list.size() - 1;
        for (; i < max; i ++) {
            stecpTeclTrans = list.get(i);
            beforeTime = stecpTeclTrans.getLocalDateTime().toEpochSecond(ZoneOffset.UTC);
            afterTime = list.get(i + 1).getLocalDateTime().toEpochSecond(ZoneOffset.UTC);
            blockList.add(stecpTeclTrans);
            // 如果时间相差超过2分钟，则需要中断分块处理
            if (afterTime - beforeTime >= GnssContext.MIN_DIF_FERENCE) {
                // 如果一个链路上的数据不满足计算标准，则直接忽略
                if (blockList.size() >= GnssContext.LINK_DATA_NUM) {
                    continuous.addAll(getContinuousTecl(blockList));
                }
                blockList.clear();
            }
        }

        if (afterTime - beforeTime < GnssContext.MIN_DIF_FERENCE) {
            blockList.add(list.get(i));
        }
        if (blockList.size() >= GnssContext.LINK_DATA_NUM) {
            continuous.addAll(getContinuousTecl(blockList));
            blockList.clear();
        }
        return continuous;
    }
    /**
     * 获取连续的TECL，如果TECL值不连续，则分块处理
     *
     * @return
     */
    private List<StecpTeclTrans> getContinuousTecl(List<StecpTeclTrans> list){
        /*if (list.size() < GnssContext.LINK_DATA_NUM) {
            return new ArrayList<>();
        }*/
        List<StecpTeclTrans> continuous = new ArrayList<>();
        List<StecpTeclTrans> blockList = new ArrayList<>();
        int i = 0, max = list.size() - 1;
        for (; i < max; i ++) {
            blockList.add(list.get(i));
            if (list.get(i).getPrn().equals(503) && list.get(i).getLocalDateTime().getMinute() == 49){
                System.out.println(blockList.get(0).getLocalDateTime().getHour());
            }
            if (Math.abs(list.get(i).getTecl() - list.get(i + 1).getTecl()) > GnssContext.POOR_VALUE) {
                if (blockList.size() >= GnssContext.LINK_DATA_NUM) {
                    continuous.addAll(getContinuousTl(blockList));
                }
                blockList.clear();
            }
        }
        if (Math.abs(list.get(i - 1).getTecl() - list.get(i).getTecl()) <= GnssContext.POOR_VALUE) {
            blockList.add(list.get(i));
        }

        if (blockList.size() >= GnssContext.LINK_DATA_NUM) {
            continuous.addAll(getContinuousTl(blockList));
            blockList.clear();
        }
        return continuous;
    }

    /**
     * 获取连续的TL，如果TL值不连续，则分块处理
     * 且，小于等于5的分为一组，大于5的分为另外一组
     * @param list
     * @return
     */
    private List<StecpTeclTrans> getContinuousTl(List<StecpTeclTrans> list){
        /*if (list.size() < GnssContext.LINK_DATA_NUM) {
            return new ArrayList<>();
        }*/
        List<StecpTeclTrans> continuous = new ArrayList<>();
        List<StecpTeclTrans> minBlock = new ArrayList<>();
        List<StecpTeclTrans> maxBlock = new ArrayList<>();
        double sub = 0;
        int i = 0, max = list.size() - 1;
        for (; i < max; i ++) {
            sub = Math.abs(GnssContext.CSPEED / GnssContext.F1 * list.get(i).getL1()
                    - GnssContext.CSPEED / GnssContext.F2 * list.get(i).getL2());
//            sub = Double.parseDouble(String.format("%.2f", sub));
            if (sub <= GnssContext.POOR_VALUE) {
                if (maxBlock.size() >= GnssContext.LINK_DATA_NUM) {
                    continuous.addAll(getContinuousStec(maxBlock));
                    continue;
                }

                maxBlock.clear();
                minBlock.add(list.get(i));
                continue;
            }

            if (minBlock.size() >= GnssContext.LINK_DATA_NUM) {
                continuous.addAll(getContinuousStec(minBlock));
            }
            minBlock.clear();
            maxBlock.add(list.get(i));
        }
        sub = Math.abs(GnssContext.CSPEED / GnssContext.F1 * list.get(i).getL1()
                - GnssContext.CSPEED / GnssContext.F2 * list.get(i).getL2());
        if (sub <= GnssContext.POOR_VALUE) {
            minBlock.add(list.get(i));
        }else {
            maxBlock.add(list.get(i));
        }
        if (minBlock.size() >= GnssContext.LINK_DATA_NUM) {
            continuous.addAll(getContinuousStec(minBlock));
            minBlock.clear();
        }
        if (maxBlock.size() >= GnssContext.LINK_DATA_NUM) {
            continuous.addAll(getContinuousStec(maxBlock));
            maxBlock.clear();
        }
        return continuous;
    }

    /**
     * 计算STEC
     *
     * @param list
     * @return
     */
    private List<StecpTeclTrans> getContinuousStec(List<StecpTeclTrans> list){
        double avgTecl = list.stream().mapToDouble(StecpTeclTrans::getTecl).average().getAsDouble();
        double avgTecp = list.stream().mapToDouble(StecpTeclTrans::getTecp).average().getAsDouble();
        double bias = 0;
        int index = 0;
        for (StecpTeclTrans teclTrans : list) {
            teclTrans.setStec(teclTrans.getTecl() - avgTecl + avgTecp);
            if (vals != null && vals.length > 0){
                index = teclTrans.getPrn() - 1;
                if (teclTrans.getPrn() >= 700) {
                    index = teclTrans.getPrn() - 701 + 63;
                }else if (teclTrans.getPrn() >= 500) {
                    index = teclTrans.getPrn() - 501 + 33;
                }
                bias = this.vals[index - 1];
            }
            teclTrans.setVtec((teclTrans.getStec() - bias)*Math.sin(teclTrans.getEle()* Math.PI / 180 ));
        }
        return list;
    }
}
